package com.ke.rule.base;

import com.google.common.base.Stopwatch;
import org.apache.commons.lang3.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * 功能部件的启动器
 * @author guyu
 *
 */
public final class FuncUnitLauncher {
  private static final Logger logger = LoggerFactory.getLogger(FuncUnitLauncher.class);
  
  // 当前的步骤类型
  private ServiceMethodContext context;
  private FuncUnitActuator actuator;
  private ValidationException.Builder builder;
  
  // 执行过的功能部件
  private List<FuncUnitSpec> executedFuncUnits;
  
  // 功能部件的异常被捕获暂存
  private Map<FuncUnitSpec, Exception> catchedExceptions;
  
  public FuncUnitLauncher(ServiceMethodContext context, FuncUnitActuator actuator, ValidationException.Builder builder) {
    super();
    this.context = context;
    this.actuator = actuator;
    this.builder = builder;
    
    this.executedFuncUnits = new ArrayList<>();
    this.catchedExceptions = new HashMap<>();
  }

  public List<FuncUnitSpec> getExecutedFuncUnits() {
    return executedFuncUnits;
  }

  public Map<FuncUnitSpec, Exception> getCatchedExceptions() {
    return catchedExceptions;
  }

  /**
   * 针对具体的一个对象开始执行功能部件
   * @param bill 待执行的功能部件规格列表
   * @param value 目标对象
   */
  public void fire(FuncUnitSpecBill bill, Object value) {
    Stopwatch watch = Stopwatch.createStarted();
//    if (logger.isDebugEnabled()) {
//      logger.debug("matched func unit spec bill: {}", bill);
//    }
    
    doFire(bill, value);
//    if (logger.isDebugEnabled()) {
//      logger.debug("fire(bill: {}, value: {}) return, cost: {} ms", bill.getAssemblyList(),
//          value.getClass().getSimpleName() + "@" + System.identityHashCode(value), watch.elapsed(TimeUnit.MILLISECONDS));
//    }
  }

  /**
   * 执行功能部件清单
   * @param bill
   * @param value
   */
  private void doFire(FuncUnitSpecBill bill, Object value) {
    for (FuncUnitSpec spec : bill.getAssemblyList()) {
      String brkdwnName = spec.getBreakdownName();
      CallLatency.begin(brkdwnName, CallBreakdown.getBreakdownExtension(context, value),
          CallBreakdown.GROUP_FUNC_UNIT);

      long start = System.currentTimeMillis();
//      if (logger.isDebugEnabled()) {
//        logger.debug("{} begin...", spec);
//      }

      boolean continueToNext = fireMatchedFuncUnit(spec, value);
//      if (logger.isDebugEnabled()) {
//        logger.debug("{} end, elapsed: {} ms", spec, (System.currentTimeMillis() - start));
//      }
      CallLatency.end(brkdwnName, CallBreakdown.getBreakdownExtension(context, value));

      // 不继续下一个部件则中断循环,也意味着当前步骤结束
      if (!continueToNext || context.isEarlyEnd()) {
//        if (logger.isInfoEnabled()) {
//          logger.info("break FuncUnitSpecBill loop: {}", spec);
//        }
        break;
      }
    }
  }
  /**
   * 根据规格执行具体功能部件
   * @param spec
   * @param value
   */
  private boolean fireMatchedFuncUnit(FuncUnitSpec spec, Object value) {
    this.executedFuncUnits.add(spec);
    
    FuncUnitAdapter funcUnit = (FuncUnitAdapter) SpringContextUtils.getBean(spec.getKlazz());
    if (null == funcUnit) { // for TestCase Mock, In fact, it's impossible to be null
      return true;
    }

    funcUnit.setSpec(spec);
    try {
      return actuator.actuate(value, funcUnit, builder);
    } catch (Exception e) {
      if (context.getCurrentStepType().isInvokeAll()) {
        logger.error(String.format("fireMatchedFuncUnit(%s, %s) error",
            spec.getName(), ClassUtils.getSimpleName(value)),  e);
        this.catchedExceptions.put(spec, e);
      } else {
        throw e;
      }
    } finally {
      funcUnit.dispose();
    }
    return true;
  }
}
